package cn.sccl.common.dao.impl;

import cn.sccl.common.dao.BaseDAO;
import cn.sccl.common.model.BaseModel;
import cn.sccl.common.model.PageResult;
import cn.sccl.common.util.GenericsUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.orm.ObjectRetrievalFailureException;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Map;

public class BaseDAOImpl<T> extends IBatisGenericDao implements BaseDAO<T> {
    /**
     * DAO所管理的Entity类型.
     */
    protected Class<T> entityClass;

    protected String primaryKeyName;

    /**
     * 在构造函数中将泛型T.class赋给entityClass.
     */
    @SuppressWarnings("unchecked")
    public BaseDAOImpl() {
        // 获取泛型类型
        entityClass = GenericsUtils.getSuperClassGenricType(getClass());
    }

    /**
     * 新架构默认要求把Mybatis的映射文件放在dao层
     * 如泛型为cn.sccl.dirms.security.model.User，则默认的命名空间是：UserDAO
     *
     * @return
     */
    @Override
    protected String getSqlMapNamespace() {
        Package pkg = this.getClass().getPackage();
        String pkgName = pkg.getName();
        if (pkgName.endsWith(".impl")) {
            return pkgName.substring(0, pkgName.indexOf("impl")) + getEntityClass().getSimpleName() + "DAO";
        } else {
            throw new RuntimeException("无法获取sql的命名空间");
        }
    }

    /*
     * (non-Javadoc)
     * @see cn.sccl.common.dao.impl.BaseDAO#findBy(java.lang.String,
     * java.lang.Object)
     */
    @Override
    public List<T> findBy(Map<String, Object> map) {
        return find(getEntityClass(), map);
    }


    @Override
    public T get(Serializable id) {
        return (T) get(getEntityClass(), id);
    }


    /*
     * (non-Javadoc)
     * @see cn.sccl.common.dao.impl.BaseDAO#getAll()
     */
    @Override
    public List<T> listAll() {
        return getAll(getEntityClass());
    }


    /**
     * 取得entityClass.
     * <p>
     * JDK1.4不支持泛型的子类可以抛开Class<T> entityClass,重载此函数达到相同效果。
     */
    protected Class<T> getEntityClass() {
        return entityClass;
    }


    /*
     * (non-Javadoc)
     * @see cn.sccl.common.dao.impl.BaseDAO#getIdName(java.lang.Class)
     */
    @Override
    @SuppressWarnings("unchecked")
    public String getIdName(Class clazz) {
        return "id";
    }


    /*
     * (non-Javadoc)
     * @see cn.sccl.common.dao.impl.BaseDAO#getPrimaryKeyName()
     */
    @Override
    public String getPrimaryKeyName() {
        if (StringUtils.isEmpty(primaryKeyName))
            primaryKeyName = "id";
        return primaryKeyName;
    }

    /*
     * (non-Javadoc)
     * @see cn.sccl.common.dao.impl.BaseDAO#setPrimaryKeyName(java.lang.String)
     */
    @Override
    public void setPrimaryKeyName(String primaryKeyName) {
        this.primaryKeyName = primaryKeyName;
    }

    protected Object getPrimaryKeyValue(T o) throws NoSuchMethodException, IllegalAccessException,
            InvocationTargetException, InstantiationException {
        if (o instanceof BaseModel) {
            return ((BaseModel) o).getId();
        } else {
            return PropertyUtils.getProperty(entityClass.cast(o), getPrimaryKeyName());
        }
    }

    /*
     * (non-Javadoc)
     * @see cn.sccl.common.dao.impl.BaseDAO#removeById(java.io.Serializable)
     */
    @Override
    public void delete(Serializable id, String... otherArgs) {
        removeById(getEntityClass(), id);
    }

    /**
     * public void delete(Object o)
     * {
     * remove(o);
     * }
     */
    /*
     * (non-Javadoc)
	 * @see cn.sccl.common.dao.impl.BaseDAO#save(java.lang.Object)
	 */
    @Override
    public void save(T o) {
        this.save(o, false);
    }

    /*
     * (non-Javadoc)
     * @see cn.sccl.common.dao.impl.BaseDAO#save(java.lang.Object)
     */
    @Override
    public void save(T o, boolean isupdateMC) {
        Object primaryKey;
        try {
            primaryKey = getPrimaryKeyValue(o);
        } catch (Exception e) {
            throw new ObjectRetrievalFailureException(entityClass, e);
        }

        if (primaryKey == null)
            insert(o);
        else
            update(o, isupdateMC);
    }

    @Override
    public List<T> query(BaseQuery query) {
        return super.query(getEntityClass(), query);
    }


    @Override
    public List<T> findByLike(Map<String, Object> map) {
        return super.findByLike(entityClass, map);
    }


    @Override
    public T findUniqueBy(Map<String, Object> map) {
        return (T) super.findUniqueBy(entityClass, map);
    }


    @Override
    public Map<String, T> findMapBy(String key, Map<String, Object> map) {
        return super.findMapBy(entityClass, key, map);
    }


    @Override
    public Map<String, T> findMapByLike(String key, Map<String, Object> map) {
        return super.findMapByLike(entityClass, key, map);
    }


    @Override
    public <T> List<T> gets(List<String> ids) {
        return (List<T>) gets(getEntityClass(), ids);
    }

    /**
     * 分页查询，所有条件要求满足完全匹配，即where语句不会应用like条件
     *
     * @param pageIndex 页码
     * @param pageSize  分页大小
     * @param params
     * @param <T>
     * @return
     */
    @Override
    public <T> PageResult<T> pageQuery(long pageIndex, int pageSize, Map<String, Object> params) {
        return super.pageQuery(getEntityClass(), pageIndex, pageSize, params);
    }

    /**
     * 分页查询，字符串属性在where语句应用like条件
     *
     * @param pageIndex 页码
     * @param pageSize  分页大小
     * @param params
     * @param <T>
     * @return
     */
    @Override
    public <T> PageResult<T> pageQueryByLike(long pageIndex, int pageSize, Map<String, Object> params) {
        return pageQueryByLike(getEntityClass(), pageIndex, pageSize, params);
    }

}
